/*
  Copyright 2006 by Sean Luke and George Mason University
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

package sim.util;

/** 
    MutableInt2D is largely a class identical to java.awt.Point, except that it is hash-equivalent to Int2D.
    It is used internally in SparseGrid2D and Continuous2D to do neighborhood lookups without having to 
    create an Int2D every time (which causes lots of garbage collection).
        
    <p>You use MutableInt2D as a STORED hash key at your peril: it has the same misfeature as Point, and you 
    should read the warning in Int2D.  However, you can look up Int2D-keyed objects in a hash table by passing in
    a MutableInt2D instead.
*/

public class MutableInt2D implements java.io.Serializable, Cloneable
    {
    public int x;
    public int y;
   
    public MutableInt2D() { x = 0; y = 0; }
    public MutableInt2D(final java.awt.Point p) { x = p.x; y = p.y; }
    public MutableInt2D(final Int2D p) { x = p.x; y = p.y; }
    public MutableInt2D(final int x, final int y) { this.x = x; this.y = y; }
    public final int getX() { return x; }
    public final int getY() { return y; }
    public final void setX(int val) { x = val; }
    public final void setY(int val) { y = val; }
    public void setTo(int x, int y) { this.x = x; this.y = y; }
    public void setTo(java.awt.Point p) { x = p.x; y = p.y; }
    public void setTo(Int2D p) { x = p.x; y = p.y; }
    public void setTo(MutableInt2D p) { x = p.x; y = p.y; }
    /** @deprecated use setTo */
    public void setLocation(int x, int y) { this.x = x; this.y = y; }
    /** @deprecated use setTo */
    public void setLocation(java.awt.Point p) { x = p.x; y = p.y; }
    /** @deprecated use setTo */
    public void setLocation(Int2D p) { x = p.x; y = p.y; }
    /** @deprecated use setTo */
    public void setLocation(MutableInt2D p) { x = p.x; y = p.y; }
    public java.awt.geom.Point2D.Double toPoint2D() { return new java.awt.geom.Point2D.Double(x,y); }
    public java.awt.Point toPoint() { return new java.awt.Point(x,y); }
    public String toString() { return "MutableInt2D["+x+","+y+"]"; }
    public String toCoordinates() { return "(" + x + ", " + y + ")"; }
    public Object clone()
        { 
        try 
            { 
            return super.clone(); 
            }
        catch(CloneNotSupportedException e)
            { 
            return null; // never happens
            } 
        }

    public final int hashCode()
        {
        int y = this.y;
        
        // basically we need to randomly disperse <int,int> --> int
        // I do that by hashing (shuffling) y, then xoring it with x. So I 
        // need something that will hash y to a nicely random value.
        // this taken from http://www.cris.com/~Ttwang/tech/inthash.htm

        // Some further discussion.  Sun's moved to a new hash table scheme
        // which has (of all things!) tables with lengths that are powers of two!
        // Normally hash table lengths should be prime numbers, in order to
        // compensate for bad hashcodes.  To fix matters, Sun now is
        // pre-shuffling the hashcodes with the following algorithm (which
        // is short but not too bad -- should we adopt it?  Dunno).  See
        // http://developer.java.sun.com/developer/bugParade/bugs/4669519.html
        //    key += ~(key << 9);
        //    key ^=  (key >>> 14);
        //    key +=  (key << 4);
        //    key ^=  (key >>> 10);
        // This is good for us because MutableInt2D, Int3D, Double2D, and Double3D
        // have hashcodes well distributed with regard to y and z, but when
        // you mix in x, they're just linear in x.  We could do a final
        // shuffle I guess.  In Java 1.3, they DON'T do a pre-shuffle, so
        // it may be suboptimal.  Since we're all moving to 1.4.x, it's not
        // a big deal since 1.4.x is shuffling the final result using the
        // Sun shuffler above.  But I'd appreciate some tests on our method
        // below, and suggestions as to whether or not we should adopt the
        // shorter, likely suboptimal but faster Sun shuffler instead
        // for y and z values.  -- Sean
        
        y += ~(y << 15);
        y ^=  (y >>> 10);
        y +=  (y << 3);
        y ^=  (y >>> 6);
        y += ~(y << 11);
        y ^=  (y >>> 16);

        // nifty!  Now mix in x
        
        return x ^ y;
        }
        
    // can't have separate equals(...) methods as the
    // argument isn't virtual
    public final boolean equals(Object obj)
        {
        if (obj==null) return false;
        else if (obj instanceof Int2D)  // do Int2D first
            {
            Int2D other = (Int2D) obj;
            return (other.x == x && other.y == y);
            }
        else if (obj instanceof MutableInt2D)
            {
            MutableInt2D other = (MutableInt2D) obj;
            return (other.x == x && other.y == y);
            }
        else if (obj instanceof Double2D)
            {
            Double2D other = (Double2D) obj;
            return (other.x == x && other.y == y);
            }
        else if (obj instanceof MutableDouble2D)
            {
            MutableDouble2D other = (MutableDouble2D) obj;
            return (other.x == x && other.y == y);
            }
        else return false;
        }


    /** Returns the distance FROM this MutableInt2D TO the specified point */
    public double distance(final double x, final double y)
        {
        final double dx = (double)this.x - x;
        final double dy = (double)this.y - y;
        return Math.sqrt(dx*dx+dy*dy);
        }

    /** Returns the distance FROM this MutableInt2D TO the specified point.   */
    public double distance(final Double2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return Math.sqrt(dx*dx+dy*dy);
        }

    /** Returns the distance FROM this MutableInt2D TO the specified point.    */
    public double distance(final MutableInt2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return Math.sqrt(dx*dx+dy*dy);
        }

    /** Returns the distance FROM this MutableInt2D TO the specified point.    */
    public double distance(final Int2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return Math.sqrt(dx*dx+dy*dy);
        }

    /** Returns the distance FROM this MutableInt2D TO the specified point.    */
    public double distance(final java.awt.geom.Point2D p)
        {
        final double dx = (double)this.x - p.getX();
        final double dy = (double)this.y - p.getY();
        return Math.sqrt(dx*dx+dy*dy);
        }

    /** Returns the squared distance FROM this MutableInt2D TO the specified point */
    public double distanceSq(final double x, final double y)
        {
        final double dx = (double)this.x - x;
        final double dy = (double)this.y - y;
        return (dx*dx+dy*dy);
        }

    /** Returns the squared distance FROM this MutableInt2D TO the specified point.    */
    public double distanceSq(final Double2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return (dx*dx+dy*dy);
        }

    /** Returns the squared distance FROM this MutableInt2D TO the specified point */
    public double distanceSq(final java.awt.geom.Point2D p)
        {
        final double dx = (double)this.x - p.getX();
        final double dy = (double)this.y - p.getY();
        return (dx*dx+dy*dy);
        }

    /** Returns the squared distance FROM this MutableInt2D TO the specified point.    */
    public double distanceSq(final MutableInt2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return (dx*dx+dy*dy);
        }

    /** Returns the squared distance FROM this MutableInt2D TO the specified point.    */
    public double distanceSq(final Int2D p)
        {
        final double dx = (double)this.x - p.x;
        final double dy = (double)this.y - p.y;
        return (dx*dx+dy*dy);
        }

    /** Returns the manhattan distance FROM this MutableInt2D TO the specified point.    */
    public long manhattanDistance(final int x, final int y)
        {
        return Math.abs((long)this.x-x) + Math.abs((long)this.y-y);
        }

    /** Returns the manhattan distance FROM this MutableInt2D TO the specified point.    */
    public long manhattanDistance(final MutableInt2D p)
        {
        return Math.abs((long)this.x-p.x) + Math.abs((long)this.y-p.y);
        }
        
    /** Returns the manhattan distance FROM this MutableInt2D TO the specified point.    */
    public long manhattanDistance(final Int2D p)
        {
        return Math.abs((long)this.x-p.x) + Math.abs((long)this.y-p.y);
        }
                
    /** Returns the manhattan distance FROM this Int2D TO the specified point.    */
    public long manhattanDistance(final java.awt.Point p)
        {
        return Math.abs((long)this.x-p.x) + Math.abs((long)this.y-p.y);
        }
    }
